package com.atguigu.tingshu.album.service.impl;


import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.anno.GuiguCache;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private AlbumAttributeValueMapper albumAttributeValueMapper ;


	@Autowired
	private AlbumStatMapper albumStatMapper ;

	@Autowired
	private KafkaService kafkaService;

	@Autowired
	private RedissonClient redissonClient;

	@Autowired
	private RedisTemplate<String , String> redisTemplate;

	@Transactional
	@Override
	public void savaAlbumInfo(AlbumInfoVo albumInfoVo) {
		//获取当前用户的登录的Id
		Long userId = AuthContextHolder.getUserId();

		//1.保存专辑的基本数据(AlbumInfo)
		AlbumInfo albumInfo = new AlbumInfo();
		albumInfo.setUserId(userId);
		//进行属性的Copy
		BeanUtils.copyProperties(albumInfoVo,albumInfo); //将albumInfoVo的属性赋值到albumInfo
		albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS); //设置专辑状态

		// 设置试听的声音的集数
		//如果是会员,可以提前看五级
		String payType = albumInfo.getPayType();		// 付费类型: 0101-免费、0102-vip免费、0103-付费
		if(!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(payType)) {
			albumInfo.setTracksForFree(5);
		}
		//保存专辑的基本数据
		albumInfoMapper.insert(albumInfo);

		//2.保存专辑的属性值数据
		List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
		albumAttributeValueVoList.stream().forEach(AlbumAttributeValueVo->{

			//封装AlbumAttributesValue对象 封装该对象要封装的数据
			AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
			albumAttributeValue.setAlbumId(albumInfo.getId());
			albumAttributeValue.setAttributeId(AlbumAttributeValueVo.getAttributeId());
			albumAttributeValue.setValueId(AlbumAttributeValueVo.getValueId());

			albumAttributeValueMapper.insert(albumAttributeValue);
		});

		// 3、初始化专辑的统计数据(album_stat)
		this.saveAlbumStat(albumInfo.getId() , SystemConstant.ALBUM_STAT_PLAY);
		this.saveAlbumStat(albumInfo.getId() , SystemConstant.ALBUM_STAT_SUBSCRIBE);
		this.saveAlbumStat(albumInfo.getId() , SystemConstant.ALBUM_STAT_BROWSE);
		this.saveAlbumStat(albumInfo.getId() , SystemConstant.ALBUM_STAT_COMMENT);

		//判断isopen的字段
		if("1".equals(albumInfo.getIsOpen())){
			kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_UPPER ,String.valueOf(albumInfo.getId()));
		}
	}

	@Override
	public IPage<AlbumListVo> findUserAlbumPage(AlbumInfoQuery albumInfoQuery, Integer pageNo, Integer pageSize) {


		//获取登陆Id 是因为实体类下的records封装两个表的数据 所有要进行关联查询  Page自带的mybatis只能单表查询 所以要自定义
		//获取当前登录用户的Id
		Long userId = AuthContextHolder.getUserId();
		albumInfoQuery.setUserId(userId);


		//创建分页对象
		Page<AlbumListVo> page = new Page<>(pageNo,pageSize);
		albumInfoMapper.findUserAlbumPage(page,albumInfoQuery);

		//返回
		return page;
	}

	@Override
	public void removeAlbumInfo(Long albumId) {
		//1.删除专辑的基本数据
		albumInfoMapper.deleteById(albumId);

		//2.删除专辑的属性值数据
		LambdaQueryWrapper<AlbumAttributeValue> lambdaQueryWrapper = new LambdaQueryWrapper<>();
		lambdaQueryWrapper.eq(AlbumAttributeValue::getAlbumId,albumId);
		albumAttributeValueMapper.delete(lambdaQueryWrapper);

		//3.删除专辑的统计数据
		LambdaQueryWrapper<AlbumStat>  albumStatLambdaQueryWrapper = new LambdaQueryWrapper<>();
		albumStatLambdaQueryWrapper.eq(AlbumStat::getAlbumId,albumId);
		//albumStatMapper.deleteById(albumId);
		albumStatMapper.delete(albumStatLambdaQueryWrapper);

		kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_LOWER,String.valueOf(albumId));
	}

	@GuiguCache(
			cacheKey = RedisConstant.ALBUM_INFO_PREFIX + "#{ #params[0] }" ,
			enableLock = true ,
			lockName = RedisConstant.ALBUM_LOCK_SUFFIX + "#{#params[0]}"
	)  // #params[0]: 表示从表达式解析器中获取params这个预设的数组中的第一个元素
	@Override
	public AlbumInfo getAlbumInfo(Long albumId) {

	/*	//这里为了解决缓存击问题,我们加入了redis的分布式锁
		String albumInfoJSon = (String) redisTemplate.opsForValue().get(RedisConstant.ALBUM_INFO_PREFIX + albumId);  //这里先向redis查询数据 并指定前缀
		if(!StringUtils.isEmpty(albumInfoJSon)){

			return JSON.parseObject(albumInfoJSon,AlbumInfo.class);
		}

		//这个加入redis锁进行判断
		String rediskey = RedisConstant.ALBUM_LOCK_SUFFIX + albumId;
		RLock lock = redissonClient.getLock(rediskey);
		boolean rlock  = lock.tryLock();
		if(rlock){
			try{
				//查询数据库
				AlbumInfo albumInfo = getAlbumInfoFromDatabase(albumId);
				if(albumInfo!=null){
					redisTemplate.opsForValue().set(RedisConstant.ALBUM_INFO_PREFIX + albumId,JSON.toJSONString(albumInfo),RedisConstant.ALBUM_TIMEOUT, TimeUnit.SECONDS);
				}

			}catch (Exception e){
				e.printStackTrace();
				return null;
			}finally {
				lock.unlock();
			}

		}else{
			return getAlbumInfo(albumId);
		}*/
		return getAlbumInfoFromDatabase(albumId);
	}


	// 加入了查询缓存的功能，并且使用了分布式锁解决了缓存击穿问题
	public AlbumInfo getAlbumInfoRedssion(Long albumId) {

		// 从Redis的缓存中查询数据
		String albumInfoJSON = redisTemplate.opsForValue().get(RedisConstant.ALBUM_INFO_PREFIX + albumId);
		if(!StringUtils.isEmpty(albumInfoJSON)) {
			log.info("从Redis中查询到了专辑数据...");
			return JSON.parseObject(albumInfoJSON , AlbumInfo.class) ;
		}

		// 获取分布式锁
		RLock rLock = redissonClient.getLock(RedisConstant.ALBUM_LOCK_SUFFIX + albumId);
		boolean tryLock = rLock.tryLock();
		if(tryLock) {

			log.info("线程{}获取到了分布式锁..." , Thread.currentThread().getId());

			try {

				// 查询数据库
				AlbumInfo albumInfo = getAlbumInfoFromDatabase(albumId);

				// 把从数据库中查询到的数据存储到Redis中
				redisTemplate.opsForValue().set(RedisConstant.ALBUM_INFO_PREFIX + albumId , JSON.toJSONString(albumInfo) , RedisConstant.ALBUM_TIMEOUT , TimeUnit.SECONDS);

				// 返回
				return albumInfo ;
			}catch (Exception e) {
				e.printStackTrace();
				return null ;
			}finally {
				rLock.unlock();
				log.info("线程{}释放了分布式锁..." , Thread.currentThread().getId());
			}

		}else  {
			log.info("线程{}没有获取到分布式锁...进行自旋" , Thread.currentThread().getId());
			return getAlbumInfo(albumId) ;   // 进行自旋
		}

	}



	//加入了查询缓存功能,并且使用了分布式锁解决了缓存击穿问题
	public AlbumInfo getAlbumInfoFromDatabase(Long albumId) {

		//根据专辑Id 查询专辑数据
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);

		//根据专辑的Id查询专辑的属性值数据
		if(albumInfo!=null){
			LambdaQueryWrapper<AlbumAttributeValue> albumInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
			albumInfoLambdaQueryWrapper.eq(AlbumAttributeValue::getAlbumId,albumId);
			List<AlbumAttributeValue> attributeValueList = albumAttributeValueMapper.selectList(albumInfoLambdaQueryWrapper);

			albumInfo.setAlbumAttributeValueVoList(attributeValueList);
		}
		return albumInfo;
	}

	@Override
	public void updateAlbumInfo(Long albumId,AlbumInfoVo albumInfoVo) {
		//1.更改专辑的基本数据
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		BeanUtils.copyProperties(albumInfoVo,albumInfo);
		albumInfoMapper.updateById(albumInfo);
		//2.更改专辑的属性值数据
		// 2.1 根据专辑id删除之前专辑的属性值数据
		LambdaQueryWrapper<AlbumAttributeValue> attributeValueLambdaQueryWrapper = new LambdaQueryWrapper<>();
		attributeValueLambdaQueryWrapper.eq(AlbumAttributeValue::getAlbumId , albumId) ;
		albumAttributeValueMapper.delete(attributeValueLambdaQueryWrapper) ;

		//2.2保存新的专辑属性数据
		List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        albumAttributeValueVoList.stream().forEach(AlbumAttributeValueVo->{

            //封装AlbumAttributesValue对象 封装该对象要封装的数据
            AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
            albumAttributeValue.setAlbumId(albumId);
            albumAttributeValue.setAttributeId(AlbumAttributeValueVo.getAttributeId());
            albumAttributeValue.setValueId(AlbumAttributeValueVo.getValueId());
            albumAttributeValueMapper.insert(albumAttributeValue);
        });

		// 判断isOpen字段的值
		if("1".equals(albumInfo.getIsOpen())) {
			kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_UPPER , String.valueOf(albumInfo.getId()));
		}else  {
			kafkaService.sendMessage(KafkaConstant.QUEUE_ALBUM_LOWER , String.valueOf(albumInfo.getId()));
		}
	}

	@Override
	public List<AlbumInfo> findUserAllAlbumList() {
		//因为是条件查询
		//获取当前登陆用户的Id
		Long userId = AuthContextHolder.getUserId();

		//创建LambdaQueryWrapper对象 封装查询条件
		LambdaQueryWrapper<AlbumInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
		//select id,Album_title from albumIn where is_deleted = 0 and userId = ?
		lambdaQueryWrapper.eq(AlbumInfo::getUserId, userId);
		lambdaQueryWrapper.select(AlbumInfo::getId,AlbumInfo::getAlbumTitle);//指定查询的数据列


		//查询专辑的列表数据
		List<AlbumInfo> albumInfoList = albumInfoMapper.selectList(lambdaQueryWrapper);

		return  albumInfoList;
	}

	// 加入了查询缓存的功能，并且使用了分布式锁解决了缓存击穿问题
	@Override
	public AlbumStatVo findAlbumStatByAlbumId(Long albumId) {

		return albumStatMapper.findAlbumStatByAlbumId(albumId);
	}

	//保存专辑的统计数据
	private void saveAlbumStat(Long albumId, String statType) {
		//创建AlbumStat对象.,封装要保存的统计数据
		AlbumStat albumStat = new AlbumStat();
		albumStat.setAlbumId(albumId);
		albumStat.setStatType(statType);
		albumStat.setStatNum(0);

		//保存专辑的统计数据
		albumStatMapper.insert(albumStat);
	}

}
